package front

import (
	"fmt"
	"golua/common"
	"golua/parser"
	"strconv"

	"github.com/antlr4-go/antlr/v4"
)

type TreeShapeListener struct {
	*parser.BaseLuaListener
	proto *common.LuacProto
}

func NewTreeShapeListener(proto *common.LuacProto) *TreeShapeListener {
	return &TreeShapeListener{
		proto: proto,
	}
}

func (this *TreeShapeListener) EnterExp(ctx *parser.ExpContext) {
	switch {
	case ctx.String_() != nil:
		s := ctx.String_()
		switch {
		case s.CHARSTRING() != nil:
			this.proto.AddConstantString(s.CHARSTRING().GetText())
			fmt.Println(s.CHARSTRING().GetText())
		default:
			panic("error string")
		}
	case ctx.Number() != nil:
		s := ctx.Number()
		switch {
		case s.INT() != nil:
			if val, err := strconv.ParseInt(s.INT().GetText(), 10, 64); err == nil {
				this.proto.AddConstantNumber(float64(val))
			} else {
				panic("error int")
			}
			fmt.Println(s.INT().GetText())
		case s.HEX() != nil:
			if val, err := strconv.ParseInt(s.HEX().GetText(), 10, 64); err == nil {
				this.proto.AddConstantNumber(float64(val))
			} else {
				panic("error hex")
			}
			fmt.Println(s.HEX().GetText())
		case s.FLOAT() != nil:
			if val, err := strconv.ParseFloat(s.FLOAT().GetText(), 64); err == nil {
				this.proto.AddConstantNumber(val)
			} else {
				panic("error float")
			}
			fmt.Println(s.FLOAT().GetText())
		case s.HEX_FLOAT() != nil:
			if val, err := strconv.ParseFloat(s.HEX_FLOAT().GetText(), 64); err == nil {
				this.proto.AddConstantNumber(val)
			} else {
				panic("error hex float")
			}
			fmt.Println(s.HEX_FLOAT().GetText())
		default:
			panic("error number")
		}
	default:
	}
}

type Luac struct {
}

func (this *Luac) Compile(fileName string) {
	input, _ := antlr.NewFileStream(fileName)
	lexer := parser.NewLuaLexer(input)
	stream := antlr.NewCommonTokenStream(lexer, 0)
	p := parser.NewLuaParser(stream)
	p.AddErrorListener(antlr.NewDiagnosticErrorListener(true))
	tree := p.Chunk()
	proto := common.NewLuaCProto()
	antlr.ParseTreeWalkerDefault.Walk(NewTreeShapeListener(proto), tree)

	buffer := common.NewBuffer()
	header := common.NewLuacHeader()
	header.WriteToBuffer(buffer)
	buffer.WriteString("")
	buffer.WriteInt32(0) //line_defined_
	buffer.WriteInt32(0) //last_line_defined_
	buffer.WriteByte(0)  //upvalues.size()
	buffer.WriteByte(0)  //fixed_params.size()
	buffer.WriteByte(0)  //is_vararg_
	buffer.WriteByte(2)  //max_stack_size
	proto.Code = append(proto.Code, common.OpLoadK(0, 0))
	proto.Code = append(proto.Code, common.OpReturn(0, 2))
	proto.WriteToBuffer(buffer)
	buffer.WriteToFile("test.luac")
}

func NewLuac() *Luac {
	return new(Luac)
}
